//
//	File Name   :	Mesh.cpp
//	Description :	Mesh implementation file.
//	Author		:	William McVicar
//  Mail		:	mcvicar.william@gmail.com
//

//  Precompiled Header
#include "CommonDefines.h"

//  Library Includes

//  Local Includes
#include "ProteinFramework.h"
#include "IFileSystem.h"
#include "IRenderer.h"
#include "loaders/3dsloader.h"
#include "loaders/fbxloader.h"
#include "RendererData.h"
#include "VertexBuffer.h"
#include "IndexBufferBase.h"
#include "IndexBuffer.h"
#include "VertexFormat.h"

//  This includes
#include "GeometryChunk.h"

//	Macros

//  Static Variables
const c8* Protein::GeometryChunk::sm_szMeshFileExtension = ".pm";

//  Static Function Prototypes

//  Implementation

namespace Protein
{

GeometryChunk::GeometryChunk()
: m_pVertexBuffer( NULL )
, m_pIndexBuffer ( NULL )
, m_pVertexFormatBase( NULL )
{

}

GeometryChunk::~GeometryChunk()
{
	delete m_pVertexBuffer;
	m_pVertexBuffer = NULL;

	delete m_pIndexBuffer;
	m_pIndexBuffer = NULL;

	delete m_pVertexFormatBase;
	m_pVertexFormatBase = NULL;
}

bool GeometryChunk::Load( const c8* _szFilename )
{
	c8 buf[ PROTEIN_MAX_PATH ];
	strcpy( buf, _szFilename );
	strcat( buf, sm_szMeshFileExtension );

	std::string strPath;
	if( IFileSystem::FILESYSTEM_ERROR_FILE_NOT_FOUND == fileSystem().FileExists( buf, strPath ) )
		return false;

	FILE* pFile = NULL;
	if( !fileSystem().Open( strPath.c_str(), g_kTEXT_READ_ONLY, &pFile, true ) )
		return false;

	u32 uiNumVerts = 0;
	u32 uiNumInds  = 0;
	c8 cDataType;
	Vector3f* pVerts = NULL;
	u32* pIndices		= NULL;

	while( !fileSystem().IsEOF( pFile ) )
	{
		u32 uiVal = 0;
		fileSystem().ReadFormattedString( pFile, "%c %d\n",&cDataType, &uiVal );

		if( cDataType == 'v' )
		{
			uiNumVerts = uiVal;
			pVerts = new Vector3f[ uiNumVerts ];
			for( u32 i = 0; i < uiNumVerts; ++i )
			{
				float x,y,z;
				fileSystem().ReadFormattedString( pFile, "%f %f %f\n", &x, &y, &z );
				pVerts[i].set( x, y, z );
			}
		}
		else if( cDataType == 'i' )
		{
			uiNumInds = uiVal;
			pIndices = new u32[ uiNumInds ];
			for( u32 i = 0; i < uiNumInds; i+=3 )
				fileSystem().ReadFormattedString( pFile, "%d %d %d\n", &pIndices[i+0], &pIndices[i+1], &pIndices[i+2] );
		}
	}

	m_uiNumIndices = uiNumInds;

	IndexBufferBase*  pIBase = new IndexBufferBase( (i32)uiNumInds, sizeof( u32 ) );
	VertexBufferBase* pVBase = new VertexBufferBase( (i32)uiNumVerts, sizeof( Vector3f ) ); 

	memcpy( pIBase->Data(), pIndices, pIBase->NumBytes() );
	memcpy( pVBase->Data(), pVerts,	  pVBase->NumBytes() );

	m_pIndexBuffer			  = new IndexBuffer( pIBase );
	m_pVertexBuffer			  = new VertexBuffer( pVBase );

	m_pVertexFormatBase = VertexFormatBase::Create( 1, ST_POSITION, SF_FLOAT3 );

	delete [] pIndices;
	delete [] pVerts;
	pIndices = NULL;
	pVerts = NULL;

	fileSystem().Close( pFile );

	return true;
}

bool GeometryChunk::Load3DS( const c8* _szFilename )
{
	std::string strPath;
	if( IFileSystem::FILESYSTEM_ERROR_FILE_NOT_FOUND == fileSystem().FileExists( _szFilename, strPath ) )
		return false;

	t3DModel model = {0};

	C3DSLoader loader;
	if( !loader.Import3DS( &model, _szFilename ) )
		return false;

	byte*	   pVerts	 = NULL;
	u32*	   pInds	 = NULL;
	i32		   iNumVerts = 0;
	i32		   iNumInds  = 0;
	i32		   iNumTexVerts = 0;
	u32		   uiVertSize = sizeof( Vector3f );

	for( i32 i = 0; i < model.iNumOfObjects; ++i )
	{
		t3DObject obj = model.object[i];
		iNumVerts += obj.iNumVerts;
		iNumInds += obj.iNumFaces * 3;
		iNumTexVerts += obj.iNumTexVertex;
	}

	if( iNumTexVerts > 0 )
		uiVertSize += sizeof( Vector2f );

	pVerts = new byte[ iNumVerts * uiVertSize ];
	pInds  = new u32[ iNumInds ];

	memset( &pVerts[0], 0, iNumVerts * uiVertSize );
	memset( &pInds[0],  0, iNumInds * sizeof( u32 ) );

	u32 uiVertsOffset = 0;
	u32 uiIndsOffset = 0;
	for( i32 i = 0; i < model.iNumOfObjects; ++i )
	{
		t3DObject obj = model.object[i];

		struct tVert { Vector3f pos; Vector2f tex; };
		tVert currentVert = { Vector3f::Zero,Vector2f::Zero };

		i32 j = 0;
		for( j = 0; j < obj.iNumVerts; ++j )
		{
			currentVert.pos = obj.pVerts[j];
			currentVert.tex = obj.pTexVerts[j];

			memcpy( &pVerts[uiVertsOffset], &currentVert, uiVertSize );
			uiVertsOffset += uiVertSize;
		}
		
		for( i32 j = 0; j < obj.iNumFaces; ++j )
		{
			memcpy( &pInds[uiIndsOffset], obj.pFaces[j].iVertIndex, sizeof( u32 ) * 3 );
			uiIndsOffset += 3;
		}
	}

	IndexBufferBase*  pIBase  = new IndexBufferBase( iNumInds, sizeof( u32 ) );
	VertexBufferBase* pVBase  = new VertexBufferBase( iNumVerts, uiVertSize );  
	memcpy( pIBase->Data(), pInds, pIBase->NumBytes() );
	memcpy( pVBase->Data(), pVerts, pVBase->NumBytes() );

	m_pIndexBuffer	= new IndexBuffer( pIBase );
	m_pVertexBuffer	= new VertexBuffer( pVBase );

	delete pIBase;
	delete pVBase;

	m_uiNumIndices = (u32)iNumInds;

	m_pVertexFormatBase = VertexFormatBase::Create( 2, ST_POSITION, SF_FLOAT3, ST_TEXCOORD, SF_FLOAT2 );

	delete [] pVerts;
	delete [] pInds;

	pVerts = NULL;
	pInds = NULL;

	return true;
}

bool GeometryChunk::LoadFBX( const c8* _szFilename )
{
	FBXLoader loader;
	tFBXData data;
	memset( &data, 0, sizeof( data ) );

	if( !loader.LoadFBX( _szFilename, data ) )
		return false;

	i32		   iNumVerts	= data.numVerts;
	i32		   iNumInds		= data.numIndices;
	i32		   iNumTexVerts = data.numTexVerts;
	u32		   uiVertSize	= sizeof( Vector3f );

	if( iNumTexVerts > 0 )
		uiVertSize += sizeof( float ) * 2;

	byte* pVerts = new byte[ iNumVerts * uiVertSize ];

	struct tVert { Vector3f pos; Vector2f tex; };
	tVert currentVert = { Vector3f::Zero,Vector2f::Zero };

	i32 i = 0;
	u32 uiVertsOffset = 0;
	for( i = 0; i < iNumVerts; ++i )
	{
		currentVert.pos = data.pVerts[i];
		currentVert.tex = data.pTexVerts[i];

		memcpy( &pVerts[uiVertsOffset], &currentVert, uiVertSize );
		uiVertsOffset += uiVertSize;
	}
	
	IndexBufferBase*  pIBase  = new IndexBufferBase( iNumInds, sizeof( u32 ) );
	VertexBufferBase* pVBase  = new VertexBufferBase( iNumVerts, uiVertSize );  
	memcpy( pIBase->Data(), &data.pInds[0],  pIBase->NumBytes() );
	memcpy( pVBase->Data(), &pVerts[0], pVBase->NumBytes() );

	m_pIndexBuffer	= new IndexBuffer( pIBase );
	m_pVertexBuffer	= new VertexBuffer( pVBase );

	delete pIBase;
	delete pVBase;

	m_uiNumIndices = (u32)iNumInds;

	m_pVertexFormatBase = VertexFormatBase::Create( 2, ST_POSITION, SF_FLOAT3, ST_TEXCOORD, SF_FLOAT2 );

	delete [] data.pVerts;
	delete [] data.pInds;
	delete [] data.pNormals;

	memset( &data, 0, sizeof( tFBXData ) );

	return true;
}

VertexBuffer* GeometryChunk::vertexBuffer()
{
	return m_pVertexBuffer;
}

IndexBuffer* GeometryChunk::indexBuffer()
{
	return m_pIndexBuffer;
}

}